package General;

import DataStructures.FileRevisionInformation;
import DataStructures.RevisionInformation;
import java.io.*;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class ProcessUtils {
    private BufferedInputStream inputStream;
    private BufferedInputStream errorStream;
        private static int filename = 0;

    public static synchronized ProcessUtils callCO(String cmdl,long timeout, String filen){
        File dir = new File(filen.substring(0,filen.lastIndexOf("/")));
        if(!dir.exists()){
            dir.mkdirs();
        }
        Process p = null;
        ProcessUtils pu = new ProcessUtils();
        try {
            List<String> cmd = new ArrayList<String>();
            for (String s : cmdl.split(" ")) {
                cmd.add(s);
            }
            ProcessBuilder pb = new ProcessBuilder(cmd);
            p = pb.start();
            pu.setErrorStream(p.getErrorStream());
            pu.setInputStream(p.getInputStream());
            long breaktime = timeout  + System.currentTimeMillis();
            Worker w = new Worker(p);
            w.start();
            while(w.isAlive() && System.currentTimeMillis() < breaktime){
                BufferedReader bri = new BufferedReader(new InputStreamReader(pu.getInputStream()));
                StringBuilder data = new StringBuilder();
                String line;
                while ((line = bri.readLine()) != null) {
                    data.append(line+"\n");
                }
                Utilities.writeToFile(data.toString(), filen, true);
                BufferedReader bre = new BufferedReader(new InputStreamReader(pu.getErrorStream()));
                StringBuilder datae = new StringBuilder();
                String linee;
                while ((linee = bre.readLine()) != null) {
                    datae.append(linee+"\n");
                }
                Utilities.writeToFile(datae.toString(), filen+"error", true);

            }
            if(w.exit != null){

                return pu;
            }
            else{
                System.out.println("INTERRUPTED GONE WRONG FOR "+cmdl);
                p.destroy();
                pu.close();
                return null;

            }
         
         }catch(IOException e){
                System.out.println("INTERRUPTED GONE WRONG FOR "+cmdl);
                p.destroy();
                return null;
         }
    }

    public void close() throws IOException {
		if (errorStream != null) {
			errorStream.close();
			errorStream = null;
		}
		if (inputStream != null) {
			inputStream.close();
			inputStream = null;
		}
	}


    private static void writeStream(InputStream inputStream, String outputFile) {
        try {
            File f = new File(outputFile);
            if(!f.exists()){
                File dir = new File(outputFile.substring(outputFile.lastIndexOf("/")));
                dir.mkdirs();
                f.createNewFile();
            }
            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
            String data = "";
            String line;
            while ((line = br.readLine()) != null) {
                data += line;
            }
            Utilities.writeToFile(data, outputFile, false);
        } catch (IOException ex) {
            try {
                System.out.println("COULD NOT WRITE TO FILE; REDIRECT TO OUTPUT!");
                BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
                String data = "";
                String line;
                while ((line = br.readLine()) != null) {
                    System.out.println(line);
                }
            } catch (IOException ex1) {
            }
        }
    }
    //maybe singleton

    public static synchronized ProcessUtils call(String cmdl){
        ProcessUtils pu = new ProcessUtils();
        try {
            List<String> cmd = new ArrayList<String>();
            for (String s : cmdl.split(" ")) {
                cmd.add(s);
            }
            ProcessBuilder pb = new ProcessBuilder(cmd);
            Process p = pb.start();
            p.waitFor();
            pu.setInputStream(p.getInputStream());
            pu.setErrorStream(p.getErrorStream());
           // BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()) );
        /*String line;
        while((line=br.readLine())!= null) {
            System.out.println("Output: "+line);
        }*/
        } catch (Exception ex) {
               ex.printStackTrace();
        }
        return pu;
    }

    /*public static void print(BufferedInputStream bis) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(bis)  );
        String line;
        while((line=br.readLine())!= null) {
            System.out.println(line);
        }
    }*/

    public void logRepo(HashMap<Integer, RevisionInformation> l, String file) throws IOException, ParserConfigurationException, InterruptedException{

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document document;
        System.out.println("XML gathered, now starting analysis");
        boolean done = false;
        while(done == false){
            try {
                document = builder.parse(new File(file));
                traverse(document.getDocumentElement(), l);
                done = true;
            } catch (SAXException ex) {
                System.out.print("NOT COMPLETE");
                Thread.sleep(100);
            }
        }
        System.out.println("Analysis completed");
    }

    public void coRepo(String url, int revision, String dir) throws IOException, InterruptedException{
        String command = "svn export -r "+revision +" --force " + url +" "+ dir;
        this.call(command);

    }

    private void traverse(Node n, HashMap<Integer, RevisionInformation> l) {
        int i;

        // find out what type of node this is

        short t = n.getNodeType();
        if (t == Document.ELEMENT_NODE) {
            //....
            if(n.getNodeName().equals("logentry")){
                RevisionInformation ri = new RevisionInformation();
                ri.setRevisionNo(Integer.parseInt(n.getAttributes().getNamedItem("revision").getNodeValue()));
                this.processLogEntry(n, ri);
                l.put(ri.getRevisionNo(), ri);
            }
            else{
                NodeList nl = n.getChildNodes();
                if (nl.getLength() == 0) {
                    return;
                }
                for (i = 0; i < nl.getLength(); i++) {
                    traverse(nl.item(i), l);
                }
            }
        }


    }

    private void processLogEntry(Node n, RevisionInformation ri){
        NodeList nl = n.getChildNodes();
            if (nl.getLength() == 0) {
                return;
            }
            for (int i = 0; i < nl.getLength(); i++) {
                Node cn = nl.item(i);
                if(cn.getNodeName().equals("author")){
                    ri.setCommiter(cn.getTextContent());
                }
                else if(cn.getNodeName().equals("date")){
                try {
                    DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
                    String d = cn.getTextContent().substring(0,cn.getTextContent().indexOf("T"));
                    Date date = (Date) formatter.parse(d);
                    ri.setDate(date);
                } catch (ParseException ex) {
                    Logger.getLogger(ProcessUtils.class.getName()).log(Level.SEVERE, null, ex);
                }

                }
                else if(cn.getNodeName().equals("paths")){
                    this.handlePaths(cn,ri.getFilerevisions(),ri.getRevisionNo());

                }
                else if(cn.getNodeName().equals("msg")){
                    ri.setComment(cn.getNodeName());
                }

            }
    }

    private void handlePaths(Node cn, ArrayList<FileRevisionInformation> filerevisions, int rno) {

        NodeList nl = cn.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
                Node ccn = nl.item(i);
                FileRevisionInformation fri = new FileRevisionInformation();
                fri.setFilename(ccn.getTextContent());
                fri.setModification(ccn.getAttributes().getNamedItem("action").getNodeValue().charAt(0));
                fri.setRevisionNo(rno);
                filerevisions.add(fri);
        }
    }

	public BufferedInputStream getErrorStream() {
		return errorStream;
	}

	/**
	 * @return the inputStream
	 */
	public BufferedInputStream getInputStream() {
		return inputStream;
	}

	/**
	 * @param errorStream the errorStream to set
	 */
	public void setErrorStream(final InputStream errorStream) {
		this.errorStream = new BufferedInputStream(errorStream);
	}

	/**
	 * @param inputStream the inputStream to set
	 */
	public void setInputStream(final InputStream inputStream) {
		this.inputStream = new BufferedInputStream(inputStream);
	}


}
